/*!	 node.h
**	 Base class for Layers and Value Nodes.
**	It defines the base members for the parent - child relationship,
**	the times where the node is modified and the handling of
**	the GUID on deletion and changing.
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007 Chris Moore
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifndef __SYNFIG_PARENTNODE_H
#define __SYNFIG_PARENTNODE_H

#include <sigc++/signal.h>
#include <set>
#include "time.h"
#include "guid.h"
#include <ETL/handle>
#include "interpolation.h"
#include "mutex.h"

// When a PasteCanvas layer has a non-zero 'time offset' parameter, should
// the waypoints shown for the canvas be adjusted?  This currently only
// partially works - see the TODO at the end of layer_pastecanvas.cpp
#define ADJUST_WAYPOINTS_FOR_TIME_OFFSET

namespace synfig
{

// TimePoint class: holds the time and the before and after interpolation mode
/**
 * It includes a GUID, to make it unique
 * \see guid.h interpolation.h
**/
class TimePoint
{
    GUID guid;
    Time time;
    Interpolation before, after;
public:

    TimePoint(const Time& x = Time::begin()):
        guid(0),
        time(x),
        before(INTERPOLATION_NIL),
        after(INTERPOLATION_NIL)
    {
    }

#ifdef _DEBUG
    const char *c_str()const;
#endif

    const GUID& get_guid()const
    {
        return guid;
    }
    const Time& get_time()const
    {
        return time;
    }
    Interpolation get_before()const
    {
        return before;
    }
    Interpolation get_after()const
    {
        return after;
    }

    void set_guid(const GUID& x)
    {
        guid = x;
    }
    void set_time(const Time& x)
    {
        time = x;
    }
    void set_before(Interpolation x)
    {
        before = x;
    }
    void set_after(Interpolation x)
    {
        after = x;
    }

    // Modify the TimePoint based on the values of \x "merging"
    // the interpolations. Used to insert a Time Point in a Time Points Set
    // \see TimePointSet::iterator TimePointSet::insert(const TimePoint& x)
    void absorb(const TimePoint& x);
}; // END of class TimePoint

inline TimePoint operator+(TimePoint lhs, const Time& rhs)
{
    lhs.set_time(lhs.get_time() + rhs);
    return lhs;
}

inline TimePoint operator-(TimePoint lhs, const Time& rhs)
{
    lhs.set_time(lhs.get_time() - rhs);
    return lhs;
}

inline bool operator<(const TimePoint& lhs, const TimePoint& rhs)
{
    return lhs.get_time() < rhs.get_time();
}

inline bool operator<(const TimePoint& lhs, const Time& rhs)
{
    return lhs.get_time() < rhs;
}

inline bool operator<(const Time& lhs, const TimePoint& rhs)
{
    return lhs < rhs.get_time();
}

inline bool operator==(const TimePoint& lhs, const TimePoint& rhs)
{
    return lhs.get_time() == rhs.get_time();
}

inline bool operator!=(const TimePoint& lhs, const TimePoint& rhs)
{
    return lhs.get_time() != rhs.get_time();
}

class TimePointSet : public std::set<TimePoint>
{
public:
    iterator insert(const TimePoint& x);

    template <typename ITER> void insert(ITER begin, ITER end)
    {
        for (; begin != end; ++begin) {
            insert(*begin);
        }
    }

}; // END of class TimePointSet

class Node : public etl::rshared_object
{


public:

    // \writeme
    typedef	TimePointSet	time_set;



private:

    // \ The GUID of the node
    GUID guid_;

    // cached time values for all the children
    mutable time_set	times;

    // \writeme
    mutable bool		bchanged;

    // The last time the node was modified since the program started
    // \see __sys_clock
    mutable int time_last_changed_;

    // \writeme
    // \see mutex.h
    mutable RWLock rw_lock_;

    // Variable used to remember that a signal_deleted has been thrown
    bool deleting_;

public:

    // A set of pointers to parent nodes
    // TODO:  This should really be private
    std::set<Node*> 	parent_set;



private:

    // Node changed signal
    sigc::signal<void> signal_changed_;

    // Child node changed signal
    sigc::signal<void, const Node*> signal_child_changed_;

    //	GUID changed signal
    /*! \note The second parameter is the *OLD* guid! */
    sigc::signal<void, GUID> signal_guid_changed_;

    //	Node deleted signal
    sigc::signal<void> signal_deleted_;



public:

    sigc::signal<void>& signal_deleted()
    {
        return signal_deleted_;
    }

    sigc::signal<void>& signal_changed()
    {
        return signal_changed_;
    }

    sigc::signal<void, const Node*>& signal_child_changed()
    {
        return signal_child_changed_;
    }

    //	GUID Changed
    /*! \note The second parameter is the *OLD* guid! */
    sigc::signal<void, GUID>& signal_guid_changed()
    {
        return signal_guid_changed_;
    }



protected:

    Node();

    // This class cannot be copied -- use clone() if necessary
private:
    Node(const Node &x);

public:
    virtual ~Node();



public:

    void changed();
    void child_changed(const Node *x);

    // Gets the GUID for this Node
    const GUID& get_guid()const;

    // Sets the GUID for this Node
    virtual void set_guid(const GUID& x);

    // Gets the time when the Node was changed
    int get_time_last_changed()const;

    // Adds the parameter \x as the child of the current Node
    void add_child(Node*x);

    // Removes the parameter \x as a child of the current Node
    void remove_child(Node*x);

    // Returns how many parenst has the current Node
    int parent_count()const;

    // Returns the cached times values for all the children
    const time_set &get_times() const;

    // Writeme!
    RWLock& get_rw_lock()const
    {
        return rw_lock_;
    }

    virtual String get_string()const = 0;
protected:

    void begin_delete();



protected:
    // Used when the node has changed. Makes changed the parent too.
    // To be overloaded by the derivative classes. Emits a signal where the
    // the GUI can be connected to.
    virtual void on_changed();

    // Used when child node has changed. Calls changed() too.
    // To be overloaded by the derivative classes. Emits a signal where the
    // the GUI can be connected to.
    virtual void on_child_changed(const Node *x);

    // Used when the node's GUID has changed.
    // To be overloaded by the derivative classes. Emits a signal where the
    // the GUI can be connected to.
    virtual void on_guid_changed(GUID guid);

    //	Function to be overloaded that fills the Time Point Set with
    // all the children Time Points.
    virtual void get_times_vfunc(time_set &set) const = 0;
}; // End of Node class

// Finds a node by its GUID.
// \see global_node_map()
synfig::Node* find_node(const synfig::GUID& guid);

// Returns a Handle to the Node by its GUID
template<typename T> etl::handle<T>
guid_cast(const synfig::GUID& guid)
{
    return etl::handle<T>::cast_dynamic(synfig::find_node(guid));
}

#ifdef _DEBUG
template <typename T>
synfig::String set_string(T start, T end)
{
    synfig::String ret("[");
    bool started = false;

    while (start != end) {
        if (started)	{
            ret += ", ";
        } else {
            started = true;
        }

        ret += synfig::String((*start).c_str());
        start++;
    }

    return ret + "]";
}

template <typename T>
synfig::String set_string(T set)
{
    return set_string(set.begin(), set.end());
}
#endif // _DEBUG

typedef etl::handle<Node> NodeHandle;

}; // END of namespace synfig

#endif